
<template>
  <div class="debug-window window">
    <header></header>
    <main class="split-pane">
      <div class="sp-sidebar">
        <header>
          <div class="title">{{ t('debugConsole.title') }}</div>
          <ButtonIcon @click="clearRequests">
            <Trash2Icon />
          </ButtonIcon>
        </header>
        <main class="list">
          <div v-if="requests.length === 0" class="empty">
            No network requests captured yet
          </div>
          <div v-else class="requests">
            <div v-for="(request, index) in requests" :key="index" class="item" :class="{ selected: selected === request }" @click="selectRequest(request)"> <span class="url">{{ request.url.split('/').slice(0, 3).join('/') }}</span>
              <span v-if="request.statusCode" class="status" :class="{ 'error': request.statusCode >= 400 }">
                {{ request.statusCode }}
              </span>
              <span v-else class="status pending" >
                <Loader />
                <Loader />
                <Loader />
              </span>
            </div>
          </div>
        </main>
      </div>

      <div class="details sp-main">
        <header>
          <div class="title" v-if="selected">{{ selected.method }} {{ selected.url }}</div>
          <div class="title" v-else>&nbsp;</div>
        </header>
        <main v-if="selected && selected.type === 'http'">
          <div class="tabs">
            <button :class="{ active: activeTab === 'request' }" @click="activeTab = 'request'">Request</button>
            <button :class="{ active: activeTab === 'response' }" @click="activeTab = 'response'">Response</button>
          </div>
          <div class="tab-content">
            <div class="section" v-if="info">
              <h3>Information</h3>
              <div class="json expanded">
                <JsonViewer :value="info" :expand-depth="1" :copyable="copyable" sort theme="jv-dark" :expanded="true" />
              </div>
            </div>
            <div v-if="activeTab === 'response' && !selected.statusCode">
              <pre>Waiting for response...</pre>
            </div>
            <template v-else>
              <div class="section" v-if="activeTab === 'request' && parameters">
                <h3>Query Parameters</h3>
                <div class="json expanded">
                  <JsonViewer :value="parameters" :expand-depth="1" :copyable="copyable" sort theme="jv-dark" :expanded="true" />
                </div>
              </div>
              <div class="section" v-if="headers">
                <h3>Headers</h3>
                <div class="json expanded">
                  <JsonViewer :value="headers" :expand-depth="1" :copyable="copyable" sort theme="jv-dark" :expanded="true" />
                </div>
              </div>
              <div class="section" v-if="data">
                <h3>Body</h3>
                <div class="json" v-if="jsonData(data)">
                  <JsonViewer :value="jsonData(data)" :expand-depth="1" :copyable="copyable" sort theme="jv-dark" :expanded="true" />
                </div>
                <pre v-else>{{ data }}<div class="copy" :class="{ copying: copying }" @click="copyData(data)">{{ t(copying ? 'common.copied' : 'common.copy').toLowerCase() }}</div></pre>
              </div>
            </template>
          </div>
        </main>
        <main v-if="selected && selected.type === 'websocket'">
          <div class="tab-content">
            <div class="section" v-if="parameters">
              <h3>Query Parameters</h3>
              <div class="json expanded">
                <JsonViewer :value="parameters" :expand-depth="1" :copyable="copyable" sort theme="jv-dark" :expanded="true" />
              </div>
            </div>
            <div class="section" v-if="headers">
              <h3>Headers</h3>
              <div class="json expanded">
                <JsonViewer :value="headers" :expand-depth="1" :copyable="copyable" sort theme="jv-dark" :expanded="true" />
              </div>
            </div>
            <div class="section" v-if="selected.frames.length > 0">
              <div class="form header">
                <h3>Frames</h3>
                <input type="checkbox" id="debug-show-sent" v-model="showSent" /><label for="debug-show-sent">Sent</label>
                &nbsp;&nbsp;&nbsp;
                <input type="checkbox" id="debug-show-received" v-model="showReceived" /><label for="debug-show-received">Received</label>
              </div>
              <template v-for="frame in selected.frames">
                <div v-if="(showSent && frame.type === 'sent') || (showReceived && frame.type === 'received')" class="json">
                  <JsonViewer :value="jsonFrame(frame)" :expand-depth="2" :copyable="copyable" theme="jv-dark" :expanded="true" />
                </div>
              </template>
            </div>
          </div>
        </main>
      </div>
    </main>
    <footer>
      <label>{{ t('common.appName') }} v{{ version }}</label>
    </footer>
  </div>
</template>

<script setup lang="ts">

import { Trash2Icon } from 'lucide-vue-next'
import { computed, onMounted, onBeforeUnmount, ref } from 'vue'
import { JsonViewer } from 'vue3-json-viewer'
import 'vue3-json-viewer/dist/vue3-json-viewer.css'
import ButtonIcon from '@components/ButtonIcon.vue'
import Loader from '@components/Loader.vue'
import { t } from '@services/i18n'
import { store } from '@services/store'
import { NetworkRequest, WebSocketFrame } from 'types'

// load store
store.loadSettings()

const requests = ref<NetworkRequest[]>([])
const selected = ref<NetworkRequest | null>(null)
const activeTab = ref<'request'|'response'>('request')
const showSent = ref(true)
const showReceived = ref(true)
const copying = ref(false)

const version = computed(() => {
  return window.api.app.getVersion()
})

const info = computed(() => {
  if (!selected.value) return null
  return {
    startTime: new Date(selected.value.startTime).toISOString(),
    ...(selected.value.endTime ? {
      endTime: new Date(selected.value.endTime).toISOString(),
      duration_ms: selected.value.endTime - selected.value.startTime,
    } : {}),
    ...(selected.value.errorMessage ? { errorMessage: selected.value.errorMessage } : {})
  }
})

const parameters = computed(() => {
  if (selected.value.url.includes('?')) {
    const urlParams = new URLSearchParams(selected.value.url.split('?')[1])
    const params: Record<string, string> = {}
    urlParams.forEach((value, key) => {
      params[key] = key.toLocaleLowerCase() === 'authorization' ? '*** hidden ***' : value
    })
    return params
  }
})

const headers = computed(() => {
  if (activeTab.value === 'request' && selected.value?.type === 'http') {
    return selected.value?.headers || {}
  } else {
    return selected.value?.responseHeaders || {}
  }
})

const data = computed(() => {
  if (activeTab.value === 'request') {
    return selected.value?.postData
  } else {
    return selected.value?.responseBody
  }
})

const copyable = computed(() => {
  return {
    copyText: t('common.copy').toLowerCase(),
    copiedText: t('common.copied').toLowerCase(),
    timeout: 1000
  }
})

onMounted(() => {
  requests.value = window.api.debug.getNetworkHistory()
  window.api.on('network', onNetworkRequest)
})

onBeforeUnmount(() => {
  window.api.off('network', onNetworkRequest)
})

const onNetworkRequest = (request: NetworkRequest) => {
  //console.log('Received Network request:', request)
  const existingIndex = requests.value.findIndex((r) => r.id === request.id)
  if (existingIndex >= 0) {
    requests.value[existingIndex] = request
    if (selected.value?.id === request.id) {
      selected.value = request
    }
  } else {
    requests.value.push(request)
  }
}

const jsonData = (data: string) => {
  try {
    return JSON.parse(data)
  } catch (e) {
    return null
  }
}

const jsonFrame = (frame: WebSocketFrame) => {
  try {
    const jsonData = JSON.parse(frame.payloadData)
    return {
      type: frame.type,
      data: jsonData,
    }
  } catch (e) {
    return {
      type: frame.type,
      data: frame.payloadData,
    }
  }
}

const copyData = (data: string) => {
  copying.value = true
  window.api.clipboard.writeText(data)
  setTimeout(() => {
    copying.value = false
  }, 1000)
}

const clearRequests = () => {
  requests.value = []
  selected.value = null
  window.api.debug.clearNetworkHistory()
}

const selectRequest = (request: NetworkRequest) => {
  selected.value = request
}

</script>


<style scoped>

.split-pane {

  .sp-sidebar {
    flex: 0 0 300px;
    main {
      padding-top: 0px;
    }
  }

  .loader {
    position: relative;
    top: -3px;
    width: 0.2rem;
    height: 0.2rem;
    margin: 0 0.1rem;
    background-color: #ff9800;
  }

  .list {
    padding: 0px 0px;
    overflow: hidden;
  }

  .requests {
    overflow-y: auto;
    scrollbar-color: var(--sidebar-scroll-thumb-color) var(--control-bg-color);
    flex: 1;
  }

  .empty {
    padding: 32px;
    text-align: center;
  }

  .item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 16px;
    border-bottom: 1px solid var(--dialog-border-color);
    cursor: pointer;
    font-size: 13.5px;
  }

  .item.selected {
    background-color: var(--sidebar-selected-color);
  }

  .url {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text-color);
  }

  .status {
    font-weight: bold;
  }

  .status {
    color: #4caf50;
  }

  .status.pending {
    color: #ff9800;
  }

  .status.error {
    color: #f44336;
  }

  .details {
    width: calc(100% - var(--large-panel-width));
    --preview-padding: 32px;
  }

  .tabs {
    padding-bottom: 8px;
  }

  .tabs button {
    padding: 8px 16px;
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    cursor: pointer;
    color: var(--text-color, #666);
    font-size: 14px;

    &:hover {
      background: none;
    }

    &.active {
      border-bottom-color: var(--color-primary, #2196F3);
      color: var(--color-primary, #2196F3);
    }

  }

  .tab-content {
    padding: 24px;
    padding-top: 0px;
    overflow-y: auto;
    scrollbar-color: var(--sidebar-scroll-thumb-color) var(--control-bg-color);
    flex: 1;
  }

  .section {
    margin-bottom: 20px;

    .header {
      display: flex;
      align-items: center;
    }    
    
    h3 {
      flex: 1;
      margin-bottom: 8px;
      font-size: 1rem;
      color: var(--text-color);
    }
  }

  pre {
    position: relative;
    white-space: pre-wrap;
    word-break: break-all;
    background-color: rgb(13,13,13);
    color: #fff;
    padding: 12px;
    padding-top: 36px;
    border-radius: 8px;
    font-size: 14px;

    .copy {
      cursor: pointer;
      position: absolute;
      right: 24px;
      top: 8px;
      font-family: SF Mono,Monaco,Andale Mono,Ubuntu Mono,monospace !important;
      font-size: 14px;
      &.copying {
        opacity: 0.4;
      }
    }
  }

  .json {
    margin-bottom: 1rem;
    background-color: rgb(13,13,13);
    border-radius: 8px;
    color: white;

    &:deep() {
      
      span, a {
        font-family: SF Mono,Monaco,Andale Mono,Ubuntu Mono,monospace !important;
        font-size: 14px;
      }

      .jv-code {
        padding: 12px;
        white-space: pre-wrap;
      }

      .jv-ellipsis {
        cursor: pointer;
      }

      .jv-toggle {
        background: none;
        &::before {
          color: var(--link-color);
          content : '⏵';
        }
        &.open {
          position: relative;
          top: 4px;
          left: 2px;
        }
      }
    
    }

    &.expanded :deep() {
      .jv-toggle {
        display: none
      }
    }
  }

}

</style>
